导语
服务 Service 介绍
服务两种启动方式
- startService 开始服务- 开始服务,会使进程变成为服务进程
- 直接开启服务,服务一旦启动跟调用者(开启者没有任何关系)
- 启动服务的activity和服务不再有一毛钱关系
- 调用者activity退出了,服务还是继续运行活的好好的。
- 调用者activity,没法访问服务里面的方法。
 
- bindService 绑定服务- 绑定服务不会使进程变成服务进程
- 绑定开启服务,服务和开启者(调用者)有密切的关系。
- 绑定服务,是activity与服务建立连接,如果activity销毁了,服务也会被解绑并销毁,但是如果服务被销毁,activity不会被销毁
- 不求同时生,但求同时死。只要activity挂了,服务跟着挂了。
- 调用者activity,可以调用服务里面的方法。
 
###绑定服务的生命周期
- 绑定服务和解绑服务的生命周期方法:onCreate->onBind->onUnbind->onDestroy
- 不会调用onstart()方法 和 onstartCommand()方法。
###绑定方式开启服务,调用服务方法的流程 (重要)eg:day08音乐播放器
- 使用bindService绑定服务的方式开启服务。  - bindService(intent, new MyConn(), Context.BIND_AUTO_CREATE);
- 参数1:绑定的意图,绑定那个服务
- 参数2:ServiceConnection对象,通讯频道(中间人的联系渠道,能找到中间人)
- 参数3:绑定方式,自动创建服务(绑定时,如果该服务不存在,自动创建该服务)
 
public class MainActivity extends Activity {
                PublicBusiness zms;//定义接口:抽取服务中的方法
                Intent intent;//定义意图:绑定那个服务
                private MyConnection conn;//定义通讯频道(UI页面与服务的连接对象),可通过通讯频道获取中间人
                @Override
                protected void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    setContentView(R.layout.activity_main);
                    intent = new Intent(this, BaoService.class);
                    conn = new MyConnection();
                      //绑定服务
                    bindService(intent, conn, BIND_AUTO_CREATE);
                }
            }
- 创建一个MyConnection类 实现服务与activity的通讯频道(UI页面和服务中间人的联系渠道)能找到中间人。返回的中间人转成接口对象类型为什么要设置中间人? 
 因为UI界面要操作服务里的方法,服务对象去调方法,而服务对象不能被new出来,所以需要在服务中创建中间人内部类,通过中间人对象调用自己类中方法,来调用服务里的方法,而服务又将中间人返给UI界面,这样UI界面就能通过中间人来操作服务里的方法了
Activity中
 class MyConnection implements ServiceConnection{
      //服务连接建立时,此方法调用。绑定服务,其实就是连接到服务,得到中间人。onBind有返回值才会调用
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
      zms = (PublicBusiness) service;
      }
//失去服务连接时调用。当服务突然异常终止的时候
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
- 服务成功绑定的时候 会执行onBinde方法,返回中间人,- 中间人中定义了许多方法,可以通过这些方法去调用服务里的方法。
- 我们只需要在UI界面Activity中得到中间人,并调用中间人的方法,就间接调用了服务的方法
 
public class BaoService extends Service {
    //服务成功绑定的时候 会执行onBinde方法,返回中间人
    @Override
    public IBinder onBind(Intent intent) {
        return new ZhongMiShu();
    }
    //定义中间人的类,将其私有,只对其他应用提供接口里的方法,通过onBinde方法返回中间人
    private class ZhongMiShu extends Binder implements PublicBusiness{
        //内部人员实现接口的方法帮助我们去调用服务的方法。
        @Override
        public void qianXian(){
            //调用服务里的方法
            banZheng();
        }
        //这些方法外部不能调用,因为ZhongMiShu中间人类是私有的。
        public void playMajiang(){
            System.out.println("一起打麻将");
        }
        //这些方法外部不能调用,因为ZhongMiShu中间人类是私有的。
        public void xisangna(){
            System.out.println("一起洗桑拿");
        }
    }
}
- 在调用者UI界面 activity代码里面通过中间人调用服务的方法。- 中间人调用在自己类中定义的方法,再通过自己的方法去调用服务里的方法
 
Activity中
 //PublicBusiness zms;//Activity中获取接口:接口中抽取中间人对外提供的方法
 //服务连接建立时,此方法调用。绑定服务,其实就是连接到服务,得到中间人。
 //点击事件:调用中间人实现接口的方法,
  public void click(View v){
       zms.qianXian();
  }
- 解除绑定服务 unbindService(conn)。
 //点击事件:解除服务
        public void unbind(View v){
            //解绑服务
            unbindService(conn);
        }
####抽取接口,隐藏私有方法。(这种方式经典,封装的思想,隐藏内部,暴露接口)
参考视频
#找领导办证
- 把服务看成一个领导,服务中有一个banZheng方法,如何才能访问?
- 绑定服务时,会触发服务的onBind方法,此方法会返回一个Ibinder的对象给MainActivity,通过这个对象访问服务中的方法
- 绑定服务
 Intent intent = new Intent(this, BanZhengService.class);
 bindService(intent, conn, BIND_AUTO_CREATE);
- 绑定服务时要求传入一个ServiceConnection实现类的对象
- 定义这个实现类
class MyServiceconn implements ServiceConnection{
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            zjr = (PublicBusiness) service;
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }
- 创建实现类对象 
 conn = new MyServiceconn();
- 在服务中定义一个类实现Ibinder接口,以在onBind方法中返回 
服务中 创建中间人对象,需继承Binder(另外将中间人要实现的方法进行抽取成接口PublicBusiness)
 class ZhongJianRen extends Binder implements PublicBusiness{
        public void QianXian(){
            //访问服务中的banZheng方法
            BanZheng();
        }
        public void daMaJiang(){
    }
}
- 把QianXian方法抽取到接口PublicBusiness中定义
#两种启动方法混合使用
- 用服务实现音乐播放时,因为音乐播放必须运行在服务进程中,可是音乐服务中的方法,需要被前台Activity所调用,所以需要混合启动音乐服务
- 先start,再bind,销毁时先unbind,在stop
##使用服务注册广播接收者
- Android四大组件都要在清单文件中注册
- 广播接收者可以使用清单文件注册- 一旦应用部署,广播接收者就生效了,直到用户手动停止应用或者应用被删除
 
- 广播接收者可以使用代码注册- 需要广播接收者运行时,使用代码注册,不需要时,可以使用代码解除注册
 
- 电量改变、屏幕开关,必须使用代码注册
1定义java类继承BroadcastReceiver(重写onReceive方法)
public class ScreenReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if(Intent.ACTION_SCREEN_OFF.equals(action)){
//            Toast.makeText(context, “关闭”, 0).show();
            System.out.println(“关闭”);
        }
        else if(Intent.ACTION_SCREEN_ON.equals(action)){
//            Toast.makeText(context, “开启”, 0).show();
            System.out.println(“开启”);
        }
    }
}
2 服务注册广播接收者(使用代码注册接收什么样的广播:屏幕开关广播)
        /**
 * 此RegisterService服务用于屏幕开关广播接收者的注册
 * @author Administrator
 *
 */
public class RegisterService extends Service {
    private ScreenReceiver receiver;//定义屏幕开关广播接收者对象
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        //创建广播接收者对象
        receiver = new ScreenReceiver();
        //通过IntentFilter对象指定广播接收者接收什么类型的广播
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        //注册广播接收者
        registerReceiver(receiver, filter);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        //解除注册
        unregisterReceiver(receiver);
    }
}
- 解除注册广播接收者 
 unregisterReceiver(receiver);
- 解除注册之后,广播接收者将失去作用 
3 只需要在UI界面,开启此RegisterService服务,就表示屏幕开关广播接收者在清单中进行了配置
//点击事件:开启服务
public void click1(View v){
      intent = new Intent(this, RegisterService.class);
      startService(intent);
}
- 4在UI界面,停止了此RegisterService服务,就表示不在接收屏幕开关时系统发出的广播
 //点击事件:停止RegisterService服务
 public void click2(View v){
 }//1.创建意图 Intent intent = new Intent(this,RegisterService.class); //2.停止服务 stopService(intent);
##本地服务:服务和启动它的组件在同一个进程
##远程服务:服务和启动它的组件不在同一个进程
- 1、服务和启动它的组件不在同一个进程时,远程服务要先在清单中进行配置配置,类似隐式启动Activity,在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点 - <service android:name="com.itheima.baopay.PayService"> <intent-filter > <action android:name="com.itheima.baopay"/> </intent-filter> </service>
- 2、服务和启动它的组件不在同一个进程时,调用者Activity界面启动远程服务时只能隐式启动,同样绑定远程服务,需要隐式绑定远程服务,绑定时第二个参数直接new,得到的接口对象需要用 pb = Stub.asInterface(service)转换类型。
- 2.1、隐式启动远程服务 - //点击事件:启动远程服务 public void click1(View v) { //隐式启动远程服务 Intent intent = new Intent(); intent.setAction("com.itheima.service"); startService(intent); }
- 2.2、隐式绑定远程服务(绑定服务,就为了操作服务中的方法)隐式绑定远程服务的第二个参数最好直接new MyConnection(),不要创建通讯频道对象传入对象。 - //点击事件:绑定远程服务 public void click2(View v) { Intent intent = new Intent(); intent.setAction("com.itheima.service"); bindService(intent, new MyConnection(), BIND_AUTO_CREATE); } // 创建一个MyConnection类,能找到中间人 class MyConnection implements ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service) { // 强转成publicbusiness pb = Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { } }
- 2.3 注意:- 服务和启动它的组件不在同一个进程时,UI界面获取的中间人,与服务返回的中间人不一致。
- 需要新技术让UI界面获取的中间人,和服务返回的中间人一致。用到AIDL。
 
- 3 使用得到的接口对象调用服务内部方法 - //点击事件:调用远程服务的方法 public void click(View v){ try { pb.banzheng(); } catch (RemoteException e) { e.printStackTrace(); } }- #AIDL 
- Android interface definition language
- 安卓接口定义语言
- 作用:跨进程通信
- 应用场景:远程服务中的中间人对象,其他应用是拿不到的,那么在通过绑定服务获取中间人对象时,就无法强制转换,使用aidl,就可以在其他应用中拿到中间人类所实现的接口
 ###AIDL实现步骤
 #####对远程服务项目的操作
- 把接口的后缀名改成aidl
- 把aidl中的public删掉
- 让中间人类直接继承stub
 #####对访问者项目的操作
- 把远程服务项目的aidl复制到本项目中
- aidl文件所在的包,必须与远程服务项目中aidl所在的包一致
- 使用stub的asInterface方法强转中间人对象 - // 创建一个MyConnection类,能找到中间人 class MyConnection implements ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service) { // 强转成publicbusiness pb = Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { } }- ###远程服务步骤示例 
 /**- 创建远程MyService服务:用于提供办证服务
- 1、先创建一个PublicBusiness接口,封装MyService服务能够对外提供的内部方法。
- 2、在MyService服务中创建中间人类继承Binder类实现PublicBusiness接口
- 3、中间人实现接口里的方法去调用MyService服务里的方法。
- 4、将中间人返回调用者Activity。
- 5、在清单中注册该MyService服务 
- 把接口的后缀名改成aidl
 
- 把aidl中的public删掉
 
- 让中间人类直接继承stub
 
- @author Administrator 
 /
 ###启动远程服务步骤示例
 /**- 此Activity用于调用远程服务里的方法
- 1、绑定远程服务(隐式启动远程服务),绑定时第二个参数直接new,
- 2、创建通信渠道类,得到中间人.得到的接口对象需要用 pb = Stub.asInterface(service)转换类型。
- 3、通过中间人调用远程服务的方法
- @author Administrator
 /
 
 
##支付宝远程服务
- 定义支付宝的服务,在服务中定义pay方法
- 定义中间人对象,把pay方法抽取成接口
- 把抽取出来的接口后缀名改成aidl
- 中间人对象直接继承Stub对象
- 注册这个支付宝服务,定义它的intent-Filter
##需要支付的应用
- 把刚才定义好的aidl文件拷贝过来,注意aidl文件所在的包名必须跟原包名一致
- 远程绑定支付宝的服务,通过onServiceConnected方法我们可以拿到中间人对象
- 把中间人对象通过Stub.asInterface方法强转成定义了pay方法的接口
- 调用中间人的pay方法
##五种前台进程
- 此进程activity执行了onresume方法,获得焦点
- 此进程拥有一个跟正在与用户交互的activity绑定的服务
- 此进程拥有一个服务执行了startForeground()方法????
- 此进程拥有一个正在执行onCreate()、onStart()或者onDestroy()方法中的任意一个的服务
- 此进程拥有一个正在执行onReceive方法的广播接收者????
##两种可见进程
- activity执行了onPause方法,失去焦点,但是可见
- 拥有一个跟可见或前台activity绑定的服务
#国际化 I18N
- Android国际化还是比较方便,一般情况下写代码是不允许直接把文本直接写在代码里,都要使用Strings。
- 代码的提示文本也需要放在资源文件目录下
- string.xml 放在对应的文件夹   - values-en-rGB- string.xml
 
- values-en-rUS      - string.xml
 
 
- values-en-rGB
- drawable   放在对应的文件夹 - rawable-en-rGB
- drawable-en-rUS
 
- 文件夹的名称 参考 IE